package com.richard.base.view

import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.os.Handler
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import com.richard.base.R
import java.util.*
import kotlin.math.abs

/**
 ***************************************
 * 项目名称:Giggle
 * @Author wuzhiguo
 * 邮箱：wuzhiguo@ksjgs.com
 * 创建时间: 2020/8/18     10:12 AM
 * 用途: 音频wave
addHeader - 添加图头的线(头尾的线不变化),百分数1-100，实际高度等于布局的高度*百分数
addFooter - 添加图尾的线(头尾的线不变化)百分数1-100，实际高度等于布局的高度*百分数
addBody - 添加图上的线,百分数1-100
lineSpace - 线间距
lineWidth - 线宽
duration - 动画持续时间
waveMode - 线条动画模式，up_down上线变化，left_right从左往右移动
lineType - 线条样式，line_graph折线样式，bar_chart柱状样式
lineColor - 线条颜色
gravity - 图的位置，center，bottom等，同androd gravity
 ***************************************
 */
class VoiceWaveView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : View(context, attrs, defStyle) {

    var bodyWaveList = LinkedList<Int>()
    var headerWaveList = LinkedList<Int>()
    var footerWaveList = LinkedList<Int>()

    private var waveList = LinkedList<Int>()

    /**
     * 线间距 px
     */
    var lineSpace: Float = 10f
    /**
     * 线宽 px
     */
    var lineWidth: Float = 20f

    var lineCount: Int = 3

    /**
     * 动画持续时间
     */
    var duration: Long = 200


    var initLineHeight: Float = 20f
    /**
     * 线颜色
     */
    private var lineColor: Int = Color.BLUE
    private var paintLine: Paint
    private var paintPathLine: Paint

    private var valueAnimator = ValueAnimator.ofFloat(0f, 1f)

    private var valueAnimatorOffset: Float = 1f

    private var valHandler = Handler()
    val linePath = Path()

    @Volatile
    var isStart: Boolean = false

    /**
     * 跳动模式
     */
    var waveMode: WaveMode = WaveMode.UP_DOWN

    /**
     * 线条样式
     */
    var lineType: LineType = LineType.BAR_CHART

    /**
     * 显示位置
     */
    var showGravity: Int = Gravity.LEFT or Gravity.BOTTOM

    private var runnable: Runnable? = null


    init {
        attrs?.let {
            val typedArray = context.theme.obtainStyledAttributes(
                attrs,
                R.styleable.VoiceWaveView, 0, 0
            )

            lineWidth = typedArray.getDimension(R.styleable.VoiceWaveView_lineWidth, 20f)
            lineSpace = typedArray.getDimension(R.styleable.VoiceWaveView_lineSpace, 10f)
            duration = typedArray.getInt(R.styleable.VoiceWaveView_duration, 200).toLong()
            showGravity = typedArray.getInt(R.styleable.VoiceWaveView_android_gravity, Gravity.LEFT or Gravity.BOTTOM)
            lineColor = typedArray.getInt(R.styleable.VoiceWaveView_lineColor, Color.BLUE)
            initLineHeight = typedArray.getDimension(R.styleable.VoiceWaveView_initLineHeight, 20f)
            val mode = typedArray.getInt(R.styleable.VoiceWaveView_waveMode, 0)
            when (mode) {
                0 -> waveMode = WaveMode.UP_DOWN
                1 -> waveMode = WaveMode.LEFT_RIGHT
                2 -> waveMode = WaveMode.UP_DOWN_IN_TURN
            }

            val lType = typedArray.getInt(R.styleable.VoiceWaveView_lineType, 0)
            when (lType) {
                0 -> lineType = LineType.BAR_CHART
                1 -> lineType = LineType.LINE_GRAPH
            }

            typedArray.recycle()
        }

        paintLine = Paint()
        paintLine.isAntiAlias = true
        paintLine.strokeCap = Paint.Cap.ROUND

        paintPathLine = Paint()
        paintPathLine.isAntiAlias = true
        paintPathLine.style = Paint.Style.STROKE

        if (bodyWaveList.isEmpty()) {
            setLineBody()
        }
    }

    /**
     * 线的高度 0,100 百分数
     */
    fun addBody(num: Int) {
        checkNum(num)
        bodyWaveList.add(num)
    }

    /**
     * 头部线的高度 0,100 百分数
     */
    fun addHeader(num: Int) {
        checkNum(num)
        headerWaveList.add(num)
    }

    /**
     * 尾部线的高度 0,100 百分数
     */
    fun addFooter(num: Int) {
        checkNum(num)
        footerWaveList.add(num)
    }

    private fun checkNum(num: Int) {
        if (num < 0 || num > 100) {
            throw Exception("num must between 0 and 100")
        }
    }

    /**
     * 开始
     */
    fun start() {
        if (isStart) {
            return
        }
        isStart = true
        if (waveMode == WaveMode.UP_DOWN || waveMode == WaveMode.UP_DOWN_IN_TURN) {
            valueAnimator.duration = duration
            valueAnimator.repeatMode = ValueAnimator.REVERSE
            valueAnimator.repeatCount = ValueAnimator.INFINITE
            valueAnimator.addUpdateListener {
                valueAnimatorOffset = it.getAnimatedValue() as Float
                invalidate()
            }
            valueAnimator.start()
        } else if (waveMode == WaveMode.LEFT_RIGHT) {
            runnable = object : Runnable {
                override fun run() {
                    val last = bodyWaveList.pollLast()
                    bodyWaveList.addFirst(last)
                    invalidate()
                    valHandler.postDelayed(this, duration);
                }
            }
            valHandler.post(runnable)
        } else {

        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        waveList.clear()
        waveList.addAll(headerWaveList)
        waveList.addAll(bodyWaveList)
        waveList.addAll(footerWaveList)

        linePath.reset()
        paintPathLine.strokeWidth = lineWidth
        paintPathLine.color = lineColor

        paintLine.strokeWidth = lineWidth
        paintLine.color = lineColor
        for (i in waveList.indices) {
            var startX = 0f
            var startY = 0f
            var endX = 0f
            var endY = 0f

            var offset = 1f
            if (i >= headerWaveList.size && i < (waveList.size - footerWaveList.size)) {//模式1 ，排除掉头尾
                offset = valueAnimatorOffset
                if (waveMode == WaveMode.UP_DOWN_IN_TURN && i % 2 == 0) {
                    offset = 1 - offset
                }
            }

            val lineHeight = waveList[i] / 100.0 * measuredHeight * offset

            val absoluteGravity = Gravity.getAbsoluteGravity(showGravity, layoutDirection);

            when (absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK) {
                Gravity.CENTER_HORIZONTAL -> {
                    val lineSize = waveList.size
                    val allLineWidth = lineSize * (lineSpace + lineWidth)
                    if (allLineWidth < measuredWidth) {
                        startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + ((measuredWidth - allLineWidth) / 2)
                    } else {
                        startX = i * (lineSpace + lineWidth) + lineWidth / 2
                    }
                    endX = startX
                }

                Gravity.RIGHT -> {
                    val lineSize = waveList.size
                    val allLineWidth = lineSize * (lineSpace + lineWidth)
                    if (allLineWidth < measuredWidth) {
                        startX = (i * (lineSpace + lineWidth) + lineWidth / 2) + (measuredWidth - allLineWidth)
                    } else {
                        startX = i * (lineSpace + lineWidth) + lineWidth / 2
                    }
                    endX = startX
                }

                Gravity.LEFT -> {
                    startX = i * (lineSpace + lineWidth) + lineWidth / 2
                    endX = startX
                }
            }


            when (showGravity and Gravity.VERTICAL_GRAVITY_MASK) {
                Gravity.TOP -> {
                    startY = 0f
                    endY = lineHeight.toFloat()
                }

                Gravity.CENTER_VERTICAL -> {
                    startY = (measuredHeight / 2 - lineHeight / 2).toFloat()
                    endY = (measuredHeight / 2 + lineHeight / 2).toFloat()
                }

                Gravity.BOTTOM -> {
                    startY = (measuredHeight - lineHeight).toFloat()
                    endY = measuredHeight.toFloat()
                }

            }
            if (lineType == LineType.BAR_CHART) {
                if (isStart) {
                    canvas?.drawLine(
                        startX,
                        startY,
                        endX,
                        endY,
                        paintLine
                    )
                } else {
                    canvas?.drawLine(
                        startX,
                        measuredHeight /2 - initLineHeight / 2,
                        endX,
                        measuredHeight / 2 + initLineHeight / 2,
                        paintLine
                    )
                }

            }
            if (lineType == LineType.LINE_GRAPH) {
                if (isStart) {
                    if (i == 0) {
                        linePath.moveTo(startX, startY)
                        val pathEndX = endX + (lineWidth / 2) + (lineSpace / 2)
                        linePath.lineTo(pathEndX, endY)
                    } else {
                        linePath.lineTo(startX, startY)
                        val pathEndX = endX + (lineWidth / 2) + (lineSpace / 2)
                        linePath.lineTo(pathEndX, endY)
                    }
                } else {
                    linePath.moveTo(startX, 0f)
                    val pathEndX = endX + (lineWidth / 2) + (lineSpace / 2)
                    linePath.lineTo(pathEndX, 0f)
                }

            }
        }
        if (lineType == LineType.LINE_GRAPH) {
            canvas?.drawPath(linePath, paintPathLine)
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
//        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val allLineWidth = bodyWaveList.size * (lineSpace + lineWidth)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT) {
            setMeasuredDimension(allLineWidth.toInt(), heightSize)
        }
    }

    /**
     * 停止 onDestroy call
     */
    fun stop() {
        isStart = false
        if (runnable != null) {
            valHandler.removeCallbacks(runnable)
        }
        valueAnimator.cancel()
        postInvalidate()
    }

    enum class WaveMode {
        UP_DOWN, LEFT_RIGHT, UP_DOWN_IN_TURN
    }

    enum class LineType {
        LINE_GRAPH, BAR_CHART
    }

    fun setLineColor(color: Int) {
        lineColor = color
        postInvalidate()
    }

    fun setLines(lines: Int) {
        this.lineCount = lines
        setLineBody()
        postInvalidate()
    }

    private fun setLineBody() {
        bodyWaveList.clear()
        for (i in 0..lineCount) {
            val x = i % 10
            val h = 100 - 88 / 5 * abs(5 - x)
            addBody(h)
        }
    }
}